import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, getRepository } from 'typeorm';
import { UserEntity } from './user.entity';
import { CreateUserDto, LoginUserDto, UpdateUserDto } from './dto';
import { SECRET } from '../config';
import { UserMessage } from './user.message';
import { validate } from 'class-validator';
import { HttpException } from '@nestjs/common/exceptions/http.exception';
import { HttpStatus } from '@nestjs/common';
import * as crypto from 'crypto';
import { ResponseData } from '../shared/base.interface';
import { ResponseCode } from '../shared/base.responseCode';

const jwt = require('jsonwebtoken');
const R = require('ramda');

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
  ) {}

  async findAll(): Promise<UserEntity[]> {
    return await this.userRepository.find();
  }

  async findOne(loginUserDto: LoginUserDto): Promise<UserEntity> {
    const findOneOptions = {
      userName: loginUserDto.userName,
      password: crypto
        .createHmac('sha256', loginUserDto.password)
        .digest('hex'),
    };

    return await this.userRepository.findOne(findOneOptions);
  }

  async create(dto: CreateUserDto): Promise<ResponseData> {
    // 检查用户名或邮箱是否存在
    const { userName, email, password, confirmPassword } = dto;

    if (password !== confirmPassword) {
      const errorInfo: ResponseData = {
        code: ResponseCode.fail,
        message: UserMessage.passwordCompareFail,
      };
      throw new HttpException(errorInfo, HttpStatus.BAD_REQUEST);
    }

    const qb = await getRepository(UserEntity)
      .createQueryBuilder('user')
      .where('user.userName = :userName', { userName })
      .orWhere('user.email = :email', { email });

    const user = await qb.getOne();

    if (user) {
      const errorInfo: ResponseData = {
        code: ResponseCode.fail,
        message: UserMessage.registerRepeatFail,
      };
      throw new HttpException(errorInfo, HttpStatus.BAD_REQUEST);
    }

    // 创建新用户
    const newUser = new UserEntity();
    newUser.userName = userName;
    newUser.email = email;
    newUser.password = password;

    const errors = await validate(newUser);
    if (errors.length > 0) {
      const errorInfo: ResponseData = {
        code: ResponseCode.fail,
        message: UserMessage.registerInputFail,
      };
      throw new HttpException(errorInfo, HttpStatus.BAD_REQUEST);
    } else {
      const savedUser = await this.userRepository.save(newUser);
      return {
        code: ResponseCode.ok,
        message: UserMessage.registerOk,
        data: this.buildUserRO(savedUser),
      };
    }
  }

  async update(dto: UpdateUserDto): Promise<ResponseData> {
    const toUpdate = await this.userRepository.findOne(dto.id);
    if (!toUpdate) {
      const errorInfo: ResponseData = {
        code: ResponseCode.fail,
        message: UserMessage.queryFail,
      };
      throw new HttpException(errorInfo, HttpStatus.BAD_REQUEST);
    }
    delete dto.id;

    if (dto.password !== dto.confirmPassword) {
      const errorInfo: ResponseData = {
        code: ResponseCode.fail,
        message: UserMessage.passwordCompareFail,
      };
      throw new HttpException(errorInfo, HttpStatus.BAD_REQUEST);
    }

    const updated = Object.assign(toUpdate, dto);
    updated.password = crypto.createHmac('sha256', 'string').digest('hex');

    const updatedUser = await this.userRepository.save(updated);
    return {
      code: ResponseCode.ok,
      message: UserMessage.updateOk,
      data: this.buildUserRO(updatedUser, false),
    };
  }

  async delete(id: string): Promise<ResponseData> {
    const deleteResult = await this.userRepository.delete(id);
    if (deleteResult.affected === 0) {
      return {
        code: ResponseCode.fail,
        message: UserMessage.queryFail,
      };
    }
    return {
      code: ResponseCode.ok,
      message: UserMessage.deleteOk,
    };
  }

  async findById(id: number): Promise<ResponseData> {
    const user = await this.userRepository.findOne(id);

    if (!user) {
      const errorInfo: ResponseData = {
        code: ResponseCode.ok,
        message: UserMessage.queryFail,
      };
      throw new HttpException(errorInfo, HttpStatus.UNAUTHORIZED);
    }

    return {
      code: ResponseCode.ok,
      message: UserMessage.queryOkByUserId,
      data: this.buildUserRO(user, false),
    };
  }

  public generateJWT(user) {
    const today = new Date();
    const exp = new Date(today);
    exp.setDate(today.getDate() + 60);

    return jwt.sign(
      {
        id: user.id,
        username: user.username,
        phone: user.phone,
        exp: exp.getTime() / 1000,
      },
      SECRET,
    );
  }

  private buildUserRO(user: UserEntity, needToken: boolean = true) {
    let userResult = R.omit(['password', 'confirmPassword'], user);

    if (needToken) {
      userResult = Object.assign(userResult, {
        token: this.generateJWT(user),
      });
    }

    return userResult;
  }
}
